/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.system;

import java.util.Arrays;
import java.util.StringTokenizer;
import tal.drivechain.parsing.Tokenizer;

/**
 *
 * @author Main
 */
public class Version {
    private String srcName;
    private int[] version;
    private Release release;
    
    public enum Release {
        PRE_ALPHA(0, "pre-alpha", "pre-a"),
        ALPHA(2, "alpha", "a"),
        BETA(4, "beta", "b"),
        RELEASE_CANDIDATE(10, "release candidate", "rc"),
        PRE_RELEASE(10, "pre-release", "pre"),
        RELEASE(50, "release", "");
        private int level;
        private String longname;
        private String shortname;
        
        Release(int level, String longname, String shortname ) {
            this.level = level;
            this.longname = longname;
            this.shortname = shortname;
        }
        
        public String getShort() { return shortname; }
        public String getLong() { return longname; }
        public int getLevel() { return level; }
        
        public static Release findRelease(String name) {
            for (Release r : Release.values())
                if (r.getLong().equals(name.toLowerCase().trim()) ||
                        r.getShort().equals(name.toLowerCase().trim()))
                    return r;
            throw new IllegalArgumentException("Unknown release version: " + name);
        }
    }
    
    public Version(String srcName, Release release, int[] versionNums) {
        if (versionNums.length>5) {
            String sver = "";
            for (long z : versionNums) sver += z + ".";
            throw new IllegalArgumentException("Invalid version number: " + sver + "(no more than 5 tokens are allowed)");
        }
        this.release = release;
        version = versionNums;
        this.srcName = srcName;
    }

    public double getVersionLevel() throws IllegalArgumentException {
        // x.y.z.a.b.c c*1 + b*100 + a*10000 + z*1000000 + y*100000000 + x*10000000000
        // for 6 version numbers max value is 
        // return = sum(num[n]*10^((n-1)*2)). has space for no more than 99 for each number.
        
        double sum = 0;
        double major = version[0] * Math.pow(10,version.length*2);
        double rel = release.getLevel() * Math.pow(10, (version.length-1)*2);
        for (int n=1; n<version.length; n++) {
            if (version[n]>99 || version[0]>99 ) {
                String sver = "";
                for (long z : version) sver += z + ".";
                throw new IllegalArgumentException("Invalid version number: " + sver);
            }
            sum += version[n]*Math.pow(10, (((version.length-n)-1)*2));
        }
        return sum + major + rel;
    }
    
    public static Version parseVersion(String ver, String defaultSrcName) {
        //format src-name release version (xx.xx.xx.xx.xx....xx.xx.xx)
        String srcName = null;
        String sRelease = null;
        String version = null;
        
        String[] l = new Tokenizer(ver, ' ').getTokens();

        if (l.length==3) {
            srcName = l[0];
            version = l[1];
            sRelease = l[2];
        } else if (l.length==2) {
            srcName = defaultSrcName;
            version = l[0];
            sRelease = l[1];
        } else if (l.length==1) {
            version = l[0];
            sRelease = "";
        } else throw new IllegalArgumentException("Invalid version string: " + ver + " (too many tokens).");
        Release release = Release.findRelease(sRelease);
        
        StringTokenizer tokenizer = new StringTokenizer(version, ".");
        int[] versionNums = new int[tokenizer.countTokens()];
        int i=0; 
        while (tokenizer.hasMoreElements()) {
            try {
                String token = tokenizer.nextToken();
                versionNums[i] = Integer.parseInt(token);
            } catch (NumberFormatException ne) {
                throw new IllegalArgumentException("Invalid version number: " + version + " may contain only numbers and dots.");
            }
            i++;
        }
        
        return new Version(srcName, release, versionNums);
    }
    
    public String getSrcName() { return srcName; }
    public Release getRelease() { return release; }
    
    public boolean isHigherThan(Version ver) {
        if (ver.getSrcName().equals(this.srcName) ||
                ver.getSrcName()==null || srcName==null)
            return (this.getVersionLevel()>ver.getVersionLevel());
        else throw new IllegalArgumentException("Can't compare versions of different sources.");
    }
    
    @Override
    public String toString() {
        return toString(true, true, true, true);
    }
    
    public String toString(boolean src, boolean num, boolean rel, boolean longrel) {
        String name;
        String vernum;
        String srel;
        
        if (src) name = srcName; else name = "";
        if (num) {
            vernum = "";
            for (int i : version)
                vernum += i +".";
            vernum = vernum.substring(0, vernum.length()-1);
        } else vernum = "";
        if (rel) {
            if (longrel) srel = " " + release.getLong();
            else srel = release.getShort();
        } else srel = "";
        
        return name + (src?" ":"") + vernum + srel;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Version) {
            if (super.equals(obj)) return true;
            else {
                Version that = (Version)obj;
                if (this.version.length!=that.version.length) return false;
                for (int i=0; i<this.version.length; i++)
                    if (this.version[i]!=that.version[i]) return false;

                return (this.release.equals(that.release) &&
                        this.srcName.equals(that.srcName));
            }
        } else return false;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 89 * hash + (this.srcName != null ? this.srcName.hashCode() : 0);
        hash = 89 * hash + Arrays.hashCode(this.version);
        hash = 89 * hash + (this.release != null ? this.release.hashCode() : 0);
        return hash;
    }

}
